センサ・シリーズ 温湿度①SHT31 その4 BLE
SHT31で測定した温湿度をBLEで通信できるようにします。ラズパイはサーバの立場のペリフェラル動作です。ラズパイで使えるpythonのBLEライブラリはさほど多くはありません。
使用環境
|
使用するpyblenoは、内部で使用するhci関連が管理者権限で動くので、次のようにインストールします。
sudo pip3 install pybleno
ソースや事例は、https://github.com/Adam-Langley/pyblenoにあります。
●examples/batteryservice/をベースに拡張する
サンプルは三つのファイルに分かれていますが、一つにします。
関数sht31()で、SHT31の読み取りはCRCは省略しました。戻り値は、温度と湿度の値(実数)のリストです。
関数sht31Service()は、サービスを定義します。UUIDは任意な文字列を使いました。Characteristics(
以下、キャラ)などでは最後の数字を変更しています。
関数sht31_temp_Characteristic()は、サービスsht31Service()の内部の定義です。read属性だけがあります。onReadRequest()は、セントラル側がread要求を出すと呼ばれます。内容は、list(struct.pack('>f', sht31()[0])) で、温度を浮動小数点形式で送ります。
起動は、sudo python3 xxx.pyです。実行を終了するのには、何らかのキーをターミナル内で打ちます。
from pybleno import*
import time
import sys
def sht31():
import smbus
i2c = smbus.SMBus(1)
i2c.write_byte_data(0x44, 0x23, 0x34)
time.sleep(0.015)
dataTmsb, dataTlsb, dataTcrc, dataHmsb, dataHlsb, dataTcrc = i2c.read_i2c_block_data(0x44, 0xff, 6)
rawT = dataTmsb << 8 | dataTlsb
rawR = dataHmsb << 8 | dataHlsb
temp = -45 + rawT * 175 / 65535
RH = 100 * rawR / 65535
# print(' temp= {0:.1f} `C, humi= {1:.1f} RH%'.format(temp, RH))
return [temp, RH]
print(sht31()[0], sht31()[1])
class sht31Service(BlenoPrimaryService):
def __init__(self):
BlenoPrimaryService.__init__(self, {
'uuid':'1234-5678-1234-5678-1234-5678-9abc-def0',
'characteristics':[sht31_temp_Characteristic()]
}
)
class sht31_temp_Characteristic(Characteristic):
def __init__(self):
Characteristic.__init__(self,{
'uuid':'1234-5678-1234-5678-1234-5678-9abc-def7',
'properties':['read'],
'value':None
}
)
def onReadRequest(self, offset, callback):
callback(Characteristic.RESULT_SUCCESS, array.array('B', list(struct.pack('>f', sht31()[0])) ))
# main
bleno = Bleno()
primaryService = sht31Service();
def onStateChange(state):
print('on->stateChange:'+state);
if(state=='poweredOn'):
bleno.startAdvertising('SHT31',[primaryService.uuid]);
else:
bleno.stopAdvertising();
bleno.on('stateChange',onStateChange)
def onAdvertisingStart(error):
print('on -> advertisingStart: ' + ('error ' + error if error else 'success'));
if not error:
def on_setServiceError(error):
print('setServices: %s' % ('error ' + error if error else 'success'))
bleno.setServices([
primaryService
], on_setServiceError)
bleno.on('advertisingStart', onAdvertisingStart)
bleno.start()
print('Hittodisconnect')
if(sys.version_info>(3,0)):
input()
else:
raw_input()
bleno.stopAdvertising()
bleno.disconnect()
print('terminated.')
sys.exit(1)
●確認
動作中のターミナルの様子です。
オン・セミコンダクターのRSL10-USB001GEVK: RSL10 USB DongleとそのツールRSL10 Bluetooth Low Enaergy Exploerをつかってセントラル側の動作をします。
local NameがSHT31なので、見つけたらConnectします。この時点でAdvertisingが止まります。Discover Serviceでサービスを表示します。UUIDの1234-5678-1234-5678-1234-5678-9abc-def7が温度データです。
読み取った0x41acd91dは浮動小数点形式です。struct.pack('>f', はビッグ・エンディアンの実数を指定しています。
Floating Point to Hex Converterのサイトに行き、この数値を入力し、変換します。21.606でした。正しく届いているようです。
●温度に加えて湿度を送る
温度のキャラsht31_temp_Characteristic(Characteristic)と同様に湿度のキャラを作ります。そしてサービスにそのキャラを追加します。
from pybleno import *
import time
import sys
def sht31():
import smbus
i2c = smbus.SMBus(1)
i2c.write_byte_data(0x44, 0x23, 0x34)
time.sleep(0.015)
dataTmsb, dataTlsb, dataTcrc, dataHmsb, dataHlsb, dataTcrc = i2c.read_i2c_block_data(0x44, 0xff, 6)
rawT = dataTmsb << 8 | dataTlsb
rawR = dataHmsb << 8 | dataHlsb
temp = -45 + rawT * 175 / 65535
RH = 100 * rawR / 65535
return [temp, RH]
print(sht31()[0], sht31()[1])
class sht31Service(BlenoPrimaryService):
def __init__(self):
BlenoPrimaryService.__init__(self, {
'uuid':'1234-5678-1234-5678-1234-5678-9abc-def0',
'characteristics':[sht31_temp_Characteristic(), sht31_humi_Characteristic()]
}
)
class sht31_temp_Characteristic(Characteristic):
def __init__(self):
Characteristic.__init__(self, {
'uuid':'1234-5678-1234-5678-1234-5678-9abc-def7',
'properties':['read'],
'value':None
}
)
def onReadRequest(self, offset, callback):
callback(Characteristic.RESULT_SUCCESS, array.array('B', list(struct.pack('>f', sht31()[0])) ))
class sht31_humi_Characteristic(Characteristic):
def __init__(self):
Characteristic.__init__(self,{
'uuid':'1234-5678-1234-5678-1234-5678-9abc-def8',
'properties':['read'],
'value':None
}
)
def onReadRequest(self, offset, callback):
callback(Characteristic.RESULT_SUCCESS, array.array('B', list(struct.pack('>f', sht31()[1])) ))
# main
bleno = Bleno()
primaryService = sht31Service()
def onStateChange(state):
print('on->stateChange:'+state)
if(state=='poweredOn'):
bleno.startAdvertising('SHT31 temp humi',[primaryService.uuid])
else:
bleno.stopAdvertising()
bleno.on('stateChange', onStateChange)
def onAdvertisingStart(error):
print('on -> advertisingStart: ' + ('error ' + error if error else 'success'));
if not error:
def on_setServiceError(error):
print('setServices: %s' % ('error ' + error if error else 'success'))
bleno.setServices([
primaryService
], on_setServiceError)
bleno.on('advertisingStart', onAdvertisingStart)
bleno.start()
print('Hittodisconnect')
if(sys.version_info > (3,0)):
input()
else:
raw_input()
bleno.stopAdvertising()
bleno.disconnect()
print('terminated.')
sys.exit(1)
|
セントラル側の情報です。
●UARTでデータを送る
ラズパイのBLEでUARTを使うメリットはわかっていません。ノルディックBLEデバイスなどを使ったボードではよく使われます。物理的にUART用のピンに何かをつなぐという利用方法があるかもしれませんが、実験していません。
UARTのUUIDは決まっています。
| UARTサービスUUID "6e400001-b5a3-f393-e0a9-e50e24dcca9e" 送信キャラUUID "6e400002-b5a3-f393-e0a9-e50e24dcca9e" 受信キャラUUID "6e400003-b5a3-f393-e0a9-e50e24dcca9e" |
sht31()関数では、温度と湿度を文字列のリストにしました。
onReadRequest()関数では、温度と湿度の区切りを';'にし、バイト列で送ります。
from pybleno import *
import time
import sys
def sht31():
import smbus
i2c = smbus.SMBus(1)
i2c.write_byte_data(0x44, 0x23, 0x34)
time.sleep(0.015)
dataTmsb, dataTlsb, dataTcrc, dataHmsb, dataHlsb, dataTcrc = i2c.read_i2c_block_data(0x44, 0xff, 6)
rawT = dataTmsb << 8 | dataTlsb
rawR = dataHmsb << 8 | dataHlsb
temp = -45 + rawT * 175 / 65535
RH = 100 * rawR / 65535
#print(str(temp))
#print(str(RH))
return [str(round(temp,2)),str(round(RH,1))]
class sht31Service(BlenoPrimaryService):
def __init__(self):
BlenoPrimaryService.__init__(self, {
'uuid':'6e400001-b5a3-f393-e0a9-e50e24dcca9e',
'characteristics':[sht31_Characteristic()]
}
)
class sht31_Characteristic(Characteristic):
def __init__(self):
Characteristic.__init__(self, {
'uuid':'6e400002-b5a3-f393-e0a9-e50e24dcca9e',
'properties':['read'],
'value':None
}
)
def onReadRequest(self, offset, callback):
callback(Characteristic.RESULT_SUCCESS, (sht31()[0]+ ';' +sht31()[1]).encode('utf-8'))
# main
bleno = Bleno()
primaryService = sht31Service()
def onStateChange(state):
print('on->stateChange:'+state)
if(state=='poweredOn'):
bleno.startAdvertising('SHT31 UART',[primaryService.uuid])
else:
bleno.stopAdvertising()
bleno.on('stateChange',onStateChange)
def onAdvertisingStart(error):
print('on -> advertisingStart: ' + ('error ' + error if error else 'success'));
if not error:
def on_setServiceError(error):
print('setServices: %s' % ('error ' + error if error else 'success'))
bleno.setServices([
primaryService
], on_setServiceError)
bleno.on('advertisingStart', onAdvertisingStart)
bleno.start()
print('Hittodisconnect')
if(sys.version_info > (3,0)):
input()
else:
raw_input()
bleno.stopAdvertising()
bleno.disconnect()
print('terminated.')
sys.exit(1)
|
セントラル側です。UTF8の文字列でみると、そのまま数字で読み取れます。